/*
    Heart of Gold engine management system
    Copyright (C) 2004 Heart of Gold development team

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/**
 * @Author Greg Feigenson 
 * @Version RC1
 * 
 * UI for the spark controller, follows the View ideal of MVC pattern.
 * All of the algorithms are in the ModelHogSpark.
 */

package com.heartOfGold.view;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JOptionPane;
import javax.swing.Box;

import com.heartOfGold.controller.Spark;

public class UiSpark extends JPanel implements ActionListener {
    /* Widgets */
    //TODO set variables private make needed accessors
    private Spark spark;
    JLabel dwellLabel = new JLabel("Dwell: ");
    JLabel rpmScaleLabel = new JLabel("RPM Focus Points");
    JLabel mapScaleLabel = new JLabel("MAP Focus Points");
    JTextField cylinders = new JTextField(10);
    JTextField[] xAxis = new JTextField[9];
    JTextField[] yAxis = new JTextField[9];
    static JTextField[] mapFields, rpmFields;
    static JTextField[][] fields;
    JLabel cylinderLabel = new JLabel("Cylinders in your car: ");
    JButton cylinderButton = new JButton("Set");
    JPanel cylinderPanel = new JPanel(new FlowLayout());
    Dimension fieldSize = new Dimension(70, 25);

    public UiSpark(final Spark sparky) {
        spark = sparky;
        setBorder(BorderFactory
                .createTitledBorder("Spark Controller Interface"));

        /* Cylinder input */
        add(cylinderPanel, BorderLayout.NORTH);
        cylinderPanel.add(cylinderLabel);
        cylinderPanel.add(cylinders);
        cylinderPanel.add(cylinderButton);
        cylinderButton.addActionListener(this);
        cylinderPanel.setBorder(BorderFactory
                .createTitledBorder("Cylinder Setup"));

        /* Button Panel */
        JPanel buttonPanel = new JPanel(new FlowLayout());
        JButton getTableButton = new JButton("Get Table");
        JButton saveTableButton = new JButton("Save Table");
        buttonPanel.add(getTableButton);
        buttonPanel.add(saveTableButton);
        buttonPanel.setBorder(BorderFactory.createTitledBorder(""));
        getTableButton.addActionListener(this);
        saveTableButton.addActionListener(this);

        /* Panels for Table */
        JPanel advancePanel = new JPanel(new GridLayout(8, 8));
        JPanel rpmPanel = new JPanel(new GridLayout(1, 8));
        JPanel mapPanel = new JPanel(new GridLayout(8, 1));

        /* MAP scale */
        mapFields = new JTextField[8];

        for (int i = 0; i < 8; i++) {
            mapFields[i] = new JTextField();
            (mapFields[i]).setPreferredSize(new Dimension(45, 15));
            (mapFields[i]).setHorizontalAlignment(JTextField.CENTER);
            mapPanel.add(mapFields[i]);
        }

        mapPanel.setBorder(BorderFactory.createTitledBorder("MAP"));

        /* RPM scale */
        rpmFields = new JTextField[8];
        for (int i = 0; i < 8; i++) {
            rpmFields[i] = new JTextField();
            (rpmFields[i]).setPreferredSize(fieldSize);
            (rpmFields[i]).setHorizontalAlignment(JTextField.CENTER);
            rpmPanel.add(rpmFields[i]);
        }
        rpmPanel.setBorder(BorderFactory.createTitledBorder("RPM"));

        /* Setup the table */
        JPanel sparkTable = new JPanel(new GridLayout(8, 8));
        fields = new JTextField[8][8];
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                fields[i][j] = new JTextField();
                (fields[i][j]).setPreferredSize(fieldSize);
                (fields[i][j]).setHorizontalAlignment(JTextField.CENTER);
                sparkTable.add(fields[i][j]);
            }
        }
        sparkTable.setBorder(BorderFactory
                .createTitledBorder("Degree of spark advance"));

        /* Assemble the table */
        Box alignV = Box.createVerticalBox();
        Box alignH = Box.createHorizontalBox();
        Box alignVMap = Box.createVerticalBox();
        int height = (int) (rpmPanel.getPreferredSize()).getHeight();
        int width = (int) (mapPanel.getPreferredSize()).getWidth();
        Dimension rig = new Dimension(width, height);
        alignVMap.add(mapPanel);
        alignVMap.add(Box.createRigidArea(rig));
        alignH.add(alignVMap);
        alignV.add(sparkTable);
        alignV.add(rpmPanel);
        alignV.add(buttonPanel);
        alignH.add(alignV);

        /* add components */
        add(alignH, BorderLayout.CENTER);
    }

    public final void actionPerformed(final ActionEvent e) {
        String command = e.getActionCommand();

        if (command.equals("Set")) {
            spark.setCylinders(stringToInt(cylinders.getText()));
        }

        if (command.equals("Get Table")) {
         // FIXME: use a popup
            if (spark.cylinderCount() == 0) {
                JOptionPane.showMessageDialog(this,
                        "Please set the number of cylinders first",
                        "Error: cylinder count not set!",
                        JOptionPane.ERROR_MESSAGE);
                return;
            }

            spark.askForTable();
        }

        if (command.equals("Save Table")) {
            // FIXME: use a popup
            if (spark.cylinderCount() == 0) {
                JOptionPane.showMessageDialog(this,
                        "Please set the number of cylinders first",
                        "Error: cylinder count not set!",
                        JOptionPane.ERROR_MESSAGE);
                return;
            }

            int i, j;
            int[] mapValues = new int[mapFields.length];
            double[] rpmValues = new double[rpmFields.length];
            double[][] tableValues = new double[8][8];

            for (i = 0; i < mapFields.length; i++) {
                mapValues[i] = stringToInt(mapFields[i].getText());
            }
            for (i = 0; i < rpmFields.length; i++) {
                rpmValues[i] = stringToDouble(rpmFields[i].getText());
            }
            for (i = 0; i < 8; i++) {
                for (j = 0; j < 8; j++) {
                    tableValues[i][j] = stringToDouble(fields[i][j].getText());
                }
            }

            spark.syncWithUI(mapValues, rpmValues, tableValues);
            spark.sendTable();
        }
    }

    public static void syncWithModel(final int[] map, final double[] rpm,
            final double[][] table) {
        int i, j;
        for (i = 0; i < mapFields.length; i++) {
            mapFields[i].setText(intToString((int) map[i]));
        }
        for (i = 0; i < rpmFields.length; i++) {
            rpmFields[i].setText(intToString((int) rpm[i]));
        }
        for (i = 0; i < 8; i++) {
            for (j = 0; j < 8; j++) {
                fields[i][j].setText(intToString((int) table[i][j]));
            }
        }
    }

    /* Private Methods */
    private static int stringToInt(final String str) {
        if (str == null || str.equals("")) {
            return 0;
        }
        return (new Integer(str).intValue());
    }

    private static double stringToDouble(final String str) {
        if (str == null || str.equals("")) {
            return 0;
        }
        return (new Double(str).doubleValue());
    }

    private static String intToString(final int i) {
        return new Integer(i).toString();
    }

    private static String doubleToString(final double d) {
        return new Double(d).toString();
    }

    private double microsecondsToDegrees(final double microSeconds,
            final double rpm) {
        return ((3000 * microSeconds * rpm) / 500) / 1000000;
    }

    private double degreesToMicroSeconds(final double degree,
            final double rpm) {
        return ((60000000 * degree) / (360 * rpm));
    }
}
